﻿using System;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace Collab.Common
{
    public static class ObservableEx
    {
        public static IObservable<T> CreateWithDisposableAsync<T>(Func<T> action)
        {
            return Observable.CreateWithDisposable<T>(o => Observable.ToAsync(action).Invoke().Subscribe(o));
        }

        //public static ObservableCollection<T> ToObservableCollection<T>(this IObservable<T> source)
        //{
        //    if (source == null)
        //        throw new ArgumentNullException("source");

        //    var collection = new ObservableCollection<T>();

        //    var weakRef = new WeakReference(collection);
        //    var subscriber = new FutureDisposable();
        //    subscriber.Set(source.Subscribe(item =>
        //    {
        //        var strongRef = weakRef.Target as ObservableCollection<T>;
        //        if (strongRef == null)
        //        {
        //            subscriber.Dispose();
        //            return;
        //        }
        //        collection.Add(item);
        //    }));
        //    return collection;
        //}


        /// <summary>
        /// Author: Dave Sexton, http://social.msdn.microsoft.com/Forums/en-US/rx/thread/36bf6ecb-70ea-4be3-aa35-b9a9cbc9a078
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="TResult"></typeparam>
        /// <param name="source"></param>
        /// <param name="property"></param>
        /// <returns></returns>
        public static IObservable<TResult> FromPropertyChanged<T, TResult>(T source, Expression<Func<T, TResult>> property)
        {
            if (source == null)
                throw new ArgumentNullException("source");
            if (property == null)
                throw new ArgumentNullException("property");

            var body = property.Body as MemberExpression;

            if (body == null)
                throw new ArgumentException("The specified expression does not reference a property.", "property");

            var propertyInfo = body.Member as PropertyInfo;

            if (propertyInfo == null)
                throw new ArgumentException("The specified expression does not reference a property.", "property");

            string propertyName = propertyInfo.Name;

            var propertyDescriptor = (from p in TypeDescriptor.GetProperties(source).Cast<PropertyDescriptor>()
                                      where string.Equals(p.Name, propertyName, StringComparison.Ordinal)
                                      select p)
                                      .Single();

            if (!propertyDescriptor.SupportsChangeEvents)
                throw new ArgumentException("The specified property does not support change events.", "property");

            var getter = property.Compile();

            return from e in Observable.FromEvent<EventHandler, EventArgs>(
                     d => d.Invoke,
                     h => propertyDescriptor.AddValueChanged(source, h),
                     h => propertyDescriptor.RemoveValueChanged(source, h))
                   select getter(source);
        }
    }
    

}
